package cn.virens.web.components.open.wxma.session;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;

import cn.virens.web.components.open.session.WxSession;
import cn.virens.web.components.open.session.WxSessionImpl;
import cn.virens.web.components.open.session.WxSessionManager;

/**
 * Spring Session 对象
 * 
 * @author virens
 */
public abstract class WxMaSessionManagerByRedis implements WxSessionManager, InitializingBean {
	public static final String prefix = "weixin:ma:session:";

	@Autowired
	@Qualifier("redisTemplate")
	private RedisTemplate<String, Object> redisTemplate;

	protected WxSession getSessionByCache(String openid) {
		return createSession(openid);
	}

	protected void putSessionByCache(String openid, WxSession session) {
		redisTemplate.expire(prefix + openid, 30 * 60, TimeUnit.SECONDS);
	}

	protected WxSession createSession(String openid, WxSession saved) {
		if (saved instanceof WxSession) {
			return saved;
		} else {
			return createSession(openid);
		}
	}

	protected WxSession createSession(String openid) {
		return new RedisSession(prefix + openid);
	}

	@Override
	public void afterPropertiesSet() throws Exception {
	}

	protected class RedisSession extends WxSessionImpl {
		private static final long serialVersionUID = -542247501447909172L;
		private BoundHashOperations<String, Object, Object> operations;

		private final String key;

		public RedisSession(String key) {
			this.key = key;
		}

		@Override
		@SuppressWarnings("unchecked")
		public <T> T getAttribute(String name) {
			return (T) oper().get(name);
		}

		@Override
		public Set<String> getAttributeNames() {
			Set<String> answer = new HashSet<>();

			for (Object item : oper().keys()) {
				answer.add(item.toString());
			}

			return answer;
		}

		@Override
		public <T> void setAttribute(String attributeName, T attributeValue) {
			oper().put(attributeName, attributeValue);
		}

		@Override
		public void removeAttribute(String attributeName) {
			oper().delete(attributeName);
		}

		@Override
		public void invalidate() {
			redisTemplate.delete(key);
		}

		@Override
		public boolean isExpired() {
			return redisTemplate.getExpire(key) > 2;
		}

		private synchronized BoundHashOperations<String, Object, Object> oper() {
			if (operations == null && key != null) {
				this.operations = redisTemplate.boundHashOps(key);
			}

			return operations;
		}
	}
}
